<template>
  <el-form-item
    v-if="analyProps.visible"
    class="merak-form-item"
    v-bind="analyProps.other"
    :prop="!!analyProps.render ? undefined : analyProps.other.prop"
  >
    <template #label="scope">
      <slot name="label" v-bind="scope" />
    </template>
    <template #error="scope">
      <slot name="error" v-bind="scope" />
    </template>
    <template v-if="!!analyProps.render">
      <slot v-if="typeof analyProps.render === 'string'" :name="analyProps.render" />
      <component v-else :is="analyProps.render" />
    </template>
    <template v-else-if="!!analyProps.type">
      <span v-if="analyProps.readonly">{{ displayText }}</span>
      <component
        v-else-if="analyProps.type in NativeComponents"
        :is="NativeComponents[analyProps.type]"
        :class="displayClass"
        autocomplete="new-password"
        v-bind="analyProps.props"
        v-model="modelValue"
        @input="handleInput"
        @change="(arg: any) => emit('change', { prop: props.prop, type: analyProps.type, data: arg })"
      >
        <template v-for="slot in Object.keys($slots).filter(slot => !excludeSlots.includes(slot))" #[slot]="scope">
          <slot :name="slot" v-bind="scope" />
        </template>
      </component>
      <component
        v-else-if="analyProps.type in ModifyComponents"
        :is="ModifyComponents[analyProps.type]"
        :class="displayClass"
        v-bind="{
          field: analyProps.field,
          type: analyProps.type,
          props: analyProps.props,
          options,
          queryOptions
        }"
        v-model:value="modelValue"
        v-model:default="modelDefault"
        v-model:ingoreQuery="modelIngoreQuery"
        @change="(arg: any) => emit('change', { prop: props.prop, type: analyProps.type, data: arg })"
      >
        <template v-for="slot in Object.keys($slots).filter(slot => !excludeSlots.includes(slot))" #[slot]="scope">
          <slot :name="slot" v-bind="scope" />
        </template>
      </component>
    </template>
  </el-form-item>
</template>

<script setup lang="ts">
import type { MerakXFormItemProps, MerakXFormItemOption, MerakXFormItemChangeParams } from "./index";

import { reactive, computed } from "vue";

import { ElFormItem, ElInput, ElDatePicker } from "element-plus";

import Select from "./components/Select.vue";
import Radio from "./components/Radio.vue";
import TreeSelect from "./components/TreeSelect.vue";

const NativeComponents = {
  input: ElInput,
  datePicker: ElDatePicker
};

const ModifyComponents = {
  select: Select,
  radio: Radio,
  treeSelect: TreeSelect
};

const props = withDefaults(defineProps<MerakXFormItemProps>(), {
  for: "#",
  visible: true,
  showMessage: true,
  required: undefined
});

const emit = defineEmits<{
  change: [params: MerakXFormItemChangeParams];
}>();

const modelValue = defineModel<any>("value");
const modelDefault = defineModel<string>("default");
const modelIngoreQuery = defineModel<boolean>("ingoreQuery");

const state = reactive<{
  options: MerakXFormItemOption[];
}>({
  options: []
});

const analyProps = computed(() => {
  const { field: key, type, props: controlProps, options, visible, readonly, render, queryOptions, ...other } = props;
  const field = key ?? other.prop;
  return {
    field,
    type,
    props: controlProps,
    options,
    visible,
    readonly,
    render,
    queryOptions,
    other
  };
});

const excludeSlots = computed(() => {
  const slots = ["label", "error"];
  if (typeof props.render === "string") {
    slots.push(props.render);
  }
  return slots;
});

const options = computed(() => {
  if (!!props.options?.length) {
    return props.options;
  }
  return state.options ?? [];
});

const displayText = computed(() => {
  switch (props.type) {
    case "select":
    case "radio":
      const label = options.value.find(option => option.value === modelValue.value)?.label;
      return !!label
        ? label
        : !!modelDefault.value
          ? modelDefault.value
          : typeof modelValue.value === "number" || !!modelValue.value
            ? modelValue.value
            : "-";
    default:
      return !!modelDefault.value
        ? modelDefault.value
        : typeof modelValue.value === "number" || !!modelValue.value
          ? modelValue.value
          : "-";
  }
});

const displayClass = computed(() => {
  switch (props.type) {
    case "input":
      return "qf_input";
    case "select":
    case "treeSelect":
      return "qf_select";
    case "datePicker":
      return "qf_date";
    default:
      return "";
  }
});

const queryOptions = async (field?: string) => {
  await props.queryOptions?.(field).then(options => {
    state.options = [...(options ?? [])];
  });
};

const handleInput = (value: any) => {
  if (props.type === "input") {
    // @ts-ignore
    const { numberType }: { numberType: { integer?: number; decimal?: string } } = props.props ?? {};
    if (!!numberType) {
      const integer: string = !!numberType.integer ? `(0|[1-9]\\d{0,${numberType.integer - 1}})` : "\\d*";
      const decimal: string = !!numberType.decimal ? `(\\.\\d{0,${numberType.decimal}})?` : "";
      const regExp: RegExp = new RegExp(integer + decimal, "g");
      modelValue.value = value.replace(/[^\d.]/g, "")?.match?.(regExp)?.[0];
    } else {
      modelValue.value = value.replace(/^\s+/g, "");
    }
  }
};
</script>

<style lang="scss">
.merak-form-item {
  .el-form-item__content > * {
    width: 100%;
  }
  .el-select {
    &:focus-within .el-select__wrapper.is-disabled {
      border-color: var(--el-color-primary);
    }
    .el-select__wrapper.is-disabled {
      background-color: var(--el-fill-color-blank);
      border-bottom: 1px solid var(--default-input-border);
      cursor: text;
      .el-select__selected-item {
        color: var(--merak-text-color);
      }
    }
  }
}
</style>
